#include "SceneNode.h"
#include "Camera.h"

namespace egg{

    SceneNode::SceneNode():mParent(0),mVisible(true){
        mNormalMatrix=NumericParamPtr(new NumericParam("uni_mat3_normal"));
        mModelviewMatrix=NumericParamPtr(new NumericParam("uni_mat4_modelview"));
    }

    void SceneNode::translate(const vec3& value,TransformSpace ts){
        vec3 relDist;
        if(ts==TS_LOCAL){
            relDist=mRelOrientation*value;
        }else if(ts==TS_PARENT){
            relDist=value;
        }else if(ts==TS_WORLD){
            relDist=mRelOrientation*(glm::inverse(mat3(getRelTransform()))*value);
        }
        setPosition(mRelPosition+relDist,TS_PARENT);
    }

    void SceneNode::setPosition(const vec3& value,TransformSpace ts){
        if(ts==TS_LOCAL){
            mRelPosition=vec3(getRelTransform()*vec4(value,1.0));
        }else if(ts==TS_PARENT){
            mRelPosition=value;
        }else if(ts==TS_WORLD){
            vec4 localPos=glm::inverse(getRelTransform())*vec4(value,1.0);
            mRelPosition=vec3(getRelTransform()*localPos);
        }
        updateTransform();
    }

    void SceneNode::rotate(float degree,const vec3& axis,TransformSpace ts){
        vec3 rotAxis;
        if(ts==TS_LOCAL){
            rotAxis=mRelOrientation*axis;
        }else if(ts==TS_PARENT){
            rotAxis=axis;
        }else if(ts==TS_WORLD){
            rotAxis=mat3(glm::inverse(getAbsTransform()))*axis;
            rotAxis=mRelOrientation*axis;
        }
        rotAxis=glm::normalize(rotAxis);
        mRelOrientation=glm::angleAxis(degree,rotAxis)*mRelOrientation;
        mRelOrientation=glm::normalize(mRelOrientation);
        updateTransform();
    }

    void SceneNode::attachChild(SceneNode* node){
        mChildren.push_back(node);
        node->mParent=this;
        node->updateTransform();
    }

    void SceneNode::attachEntity(Entity3D* entity){
        mEntities.push_back(entity);
        entity->_onAttached(this);
    }

    void SceneNode::onCull(const FrameEvent& evt){
        const mat4& viewMat=evt.mCamera->getViewMatrix();
        mModelviewMatrix->set<mat4>(viewMat*getAbsTransform());
        mNormalMatrix->set<mat3>(mat3(glm::transpose(glm::inverse(mModelviewMatrix->getConstantRef<mat4>()))));
    }

    void SceneNode::onRender(const FrameEvent& evt){

    }

    //======================================================================

    void SceneNode::updateTransform(){
        if(mParent){
            mAbsTransform=mParent->getAbsTransform()*getRelTransform();
            for(int i=0;i<mChildren.size();i++){
                mChildren[i]->updateTransform();
            }
        }
    }

}